/**
 * 
 */
package lists;
import tester.cobertura.Instrument;

/**
 * A ConsList is a List with a first and a rest?
 * 
 * @author Adam
 */
@Instrument public class ConsList<T> extends List<T> {
  /**
   * 
   */
  private static final long serialVersionUID = 1L;
  protected T               fst;
  protected List<T>         rst;
  
  // Constructor:
  /**
   * @param fst First
   * @param rst Rest
   */
  public ConsList(T fst, List<T> rst) {
    this.fst = fst;
    this.rst = rst;
    // TODO Auto-generated constructor stub
  }
  
  // OrMap
  /*
   * (non-Javadoc)
   * @see lists.List#ormap(lists.UnaryFunction)
   */
  @Override public boolean ormap(UnaryFunction<Boolean, T> f) {
    // Runs the Predicate, or Unary Function, on the first element
    // and "ors" it to the ormap of the rest.
    return f.f(this.fst) || this.rst.ormap(f);
  }
  
  // Size
  /*
   * (non-Javadoc)
   * @see lists.List#size()
   */
  @Override public int size() {
    // Adds one to the size of the rest.
    return 1 + this.rst.size();
  }
  
  // Filter
  /*
   * (non-Javadoc)
   * @see lists.List#filter(lists.UnaryFunction)
   */
  @Override public List<T> filter(UnaryFunction<Boolean, T> f) {
    // If the first element satisfies the Predicate UnaryFunction, it
    // adds it to the filter of the rest.
    if (f.f(this.fst)) {
      return new ConsList<T>(this.fst, this.rst.filter(f));
      // Else it filters the rest.
    } else {
      return this.rst.filter(f);
    }
  }
  
  /*
   * (non-Javadoc)
   * @see lists.List#map(lists.UnaryFunction)
   */
  @Override public <R> List<R> map(UnaryFunction<R, T> f) {
    // Applies the UnaryFunction to the first element of the list
    // and creates a list with that and the mapped rest of the list.
    return new ConsList<R>(f.f(this.fst), this.rst.map(f));
  }
  
  /*
   * (non-Javadoc)
   * @see lists.List#count(lists.UnaryFunction)
   */
  @Override public int count(UnaryFunction<Boolean, T> f) {
    // If the first element satisfies the Predicate UnaryFunction it
    // adds one to counted rest of the list.
    if (f.f(this.fst)) {
      return 1 + this.rst.count(f);
      // Else it counts the rest of the list.
    } else {
      return this.rst.count(f);
    }
  }
  
  /**
   * @return the rst
   */
  @Override public String toString() {
    // TODO Auto-generated method stub
    return this.fst.toString() + "\n" + this.rst.toString();
  }
  
  /*
   * (non-Javadoc)
   * @see lists.List#insert(lists.BinaryFunction, java.lang.Object)
   */
  @Override public List<T> insert(BinaryFunction<Boolean, T, T> f, T t) {
    // If it is in the right place put it there.
    if (f.f(t, this.getFst())) {
      return new ConsList<T>(t, this);
      // Else look in the rest of the list.
    } else {
      return new ConsList<T>(this.getFst(), this.getRst().insert(f, t));
    }
  }
  
  /*
   * (non-Javadoc)
   * @see lists.List#sort(lists.BinaryFunction)
   */
  @Override public List<T> sort(BinaryFunction<Boolean, T, T> f) {
    // Sorts the list based on the BinaryFunction.
    return this.getRst().sort(f).insert(f, this.getFst());
  }

  /* (non-Javadoc)
   * @see lists.List#foldr(lists.BinaryFunction, java.lang.Object)
   */
  @Override public <R> R foldr(BinaryFunction<R, T, R> f, R base) {
    // TODO Auto-generated method stub
    return null;
  }

  /**
   * @return the fst
   */
  public T getFst() {
    return fst;
  }
  
  /**
   * @return the rst
   */
  public List<T> getRst() {
    return rst;
  }
}
